﻿using System;
using System.Linq;
using WinRTFramework.FrameworkConfiguration;
using WinRTFramework.Interfaces.Contracts.Search;
using WinRTFramework.Interfaces.Navigation;
using Windows.ApplicationModel.Search;
using Windows.UI.Xaml.Controls;

namespace WinRTFramework.FrameworkCore.Managers
{
    internal class SearchManager
    {
        private readonly Frame _rootFrame;
        private IIoCAdapter _ioCAdapter;
        private INavigationService _navigationService;

        internal SearchManager(Frame rootFrame, IIoCAdapter ioCAdapter, INavigationService navigationService)
        {
            _rootFrame = rootFrame;
            _ioCAdapter = ioCAdapter;
            _navigationService = navigationService;

            SearchPane.GetForCurrentView().QuerySubmitted += OnQuerySubmitted;
            SearchPane.GetForCurrentView().SuggestionsRequested += OnSearchPaneSuggestionsRequested;
        }

        public ISearchActivationViewModelTypeSelector SearchActivationViewModelTypeSelector { get; set; }

        public void HandleSearchActivation(string language, string queryText)
        {
            NavigateToPageThatSupportsSearchActivation(language, queryText);

            var currentPage = _rootFrame.Content as Page;
            if (currentPage == null)
                return;

            TryCallProcessActivationQueryTextOnCurrentPage(currentPage, language, queryText);
        }

        private void TryCallProcessActivationQueryTextOnCurrentPage(Page currentPage, string language, string queryText)
        {
            ISupportSearchActivation supportingSearchActivation = (ISupportSearchActivation)currentPage.DataContext;
            supportingSearchActivation.ProcessActivationQueryText(language, queryText);
        }

        private void NavigateToPageThatSupportsSearchActivation(string language, string queryText)
        {
            _navigationService.Navigate(GetViewModelTypeThatSupportsSearchActivation(language, queryText));
        }

        Type GetViewModelTypeThatSupportsSearchActivation(string language, string queryText)
        {
            Type viewModelThatSupportsSearchActivation = null;

            if (SearchActivationViewModelTypeSelector != null)
                viewModelThatSupportsSearchActivation = GetViewModelTypeFromSelector(language, queryText);
            else
                viewModelThatSupportsSearchActivation = GetViewModelTypeFromIoCContainer();

            return viewModelThatSupportsSearchActivation;
        }

        private Type GetViewModelTypeFromIoCContainer()
        {
            Type viewModelThatSupportsSearchActivation =
                _ioCAdapter.GetAllRegisteredTypesThatImplement<ISupportSearchActivation>().FirstOrDefault();

            if (viewModelThatSupportsSearchActivation == null)
                throw new InvalidOperationException(
                    "No ViewModel found in IoC Container which implements ISupportSearchActivation. SearchManager can only handle search activations when such a ViewModel is registered.");

            return viewModelThatSupportsSearchActivation;
        }

        private Type GetViewModelTypeFromSelector(string language, string queryText)
        {
            Type viewModelType = SearchActivationViewModelTypeSelector.GetViewModelTypeForSearchActivation(language,
                                                                                                           queryText);
            if(viewModelType == null)
                throw new InvalidOperationException("SearchActivationViewModelTypeSelector has to return a ViewModel Type for the specified search argument");

            return viewModelType;
        }

        private void OnSearchPaneSuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
        {
            var currentPage = _rootFrame.Content as Page;
            if (currentPage == null)
                return;

            TryRequestSearchSuggestionsFromCurrentPage(currentPage, args);
        }

        private void TryRequestSearchSuggestionsFromCurrentPage(Page currentPage, SearchPaneSuggestionsRequestedEventArgs args)
        {
            ISupportSearchContract supportingSearchContract = currentPage.DataContext as ISupportSearchContract;
            if (supportingSearchContract == null)
                return;

            supportingSearchContract.OnSearchPaneSuggestionsRequested(args);
        }

        private void OnQuerySubmitted(SearchPane sender, SearchPaneQuerySubmittedEventArgs args)
        {
            var currentPage = _rootFrame.Content as Page;
            if (currentPage == null)
                return;

            TryCallProcessQueryTextOnCurrentPage(currentPage, args);
        }

        private void TryCallProcessQueryTextOnCurrentPage(Page currentPage, SearchPaneQuerySubmittedEventArgs args)
        {
            ISupportSearchContract supportingSearchContract = currentPage.DataContext as ISupportSearchContract;
            if (supportingSearchContract == null)
                return;

            supportingSearchContract.ProcessQueryText(args);
        }
    }
}
